//
//  ViewController.m
//  RAC学习
//
//  Created by Hello Cai on 2020/1/21.
//  Copyright © 2020 Hello Cai. All rights reserved.
//

#import "ViewController.h"
#import "HRView.h"
#import <ReactiveObjC.h>
#import <NSObject+RACKVOWrapper.h>
#import <ReactiveObjC/RACReturnSignal.h>

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UITextField *textfield;
@property (weak, nonatomic) IBOutlet HRView *hrView;
@property (weak, nonatomic) IBOutlet UIButton *btn;
@property (weak, nonatomic) IBOutlet UILabel *label;

@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, assign) int time;
@property (nonatomic, strong) RACDisposable *timerDisposable;
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
//    [self signalDemeo];
    
//    [self subjectDemo];
    
//    [self commandDemo];
    
    [self mapDemo];
    
    //核心方法
//    [self bindDemo];
    
//    [self viewActionSignalDemo];
    
//    [self otherDemo];
    
//    [self timerSignalDemo];
}

-(void)timerSignalDemo{
    
    /* 子线程中创建定时器执行任务例子
     thread声明为属性是没用的，因为声明只能让oc对象self.thread活着，但底层的线程还是死了
     要想线程不死，必须有执行不完的任务
     */
//    __weak typeof(self) weakSelf =self;
//    NSThread *thread = [[NSThread alloc] initWithBlock:^{
//
//        NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerGo) userInfo:nil repeats:YES];
//        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
//        weakSelf.timer = timer;
//
//        //runloop跑循环，子线程就能不死（调用run这种方式跑循环，runloop无法退出）
//        [[NSRunLoop currentRunLoop] run];
//        NSLog(@"任务结束了");
//    }];
//    [thread start];
    
    //RAC定时器内部用的是GCD原理。scheduler是子线程，mainThreadScheduler是主线程
    self.time = 5;
    @weakify(self);
    self.timerDisposable = [[RACSignal interval:1.0 onScheduler:[RACScheduler scheduler]] subscribeNext:^(NSDate * _Nullable x) {
        @strongify(self);
        NSLog(@"定时器：%@--%@", x, [NSThread currentThread]);
        self.time--;
        if (self.time==0) {
            if (self.time == 0) {
                [self.timerDisposable dispose];
                NSLog(@"定时器停止了");
            }
        }
    }];
    
}

-(void)timerGo{
    NSLog(@"timer来了,%@",[NSThread currentThread]);
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    
    if (touches.anyObject.view == self.hrView) {
        
    }else{
        self.hrView.frame = CGRectMake(50, 50, 200, 200);
        //发起通知
        [[NSNotificationCenter defaultCenter] postNotificationName:@"CustomNotification" object:nil userInfo:@{@"Notify":@"make by touchBegan"}];
    }
}

-(void)otherDemo{
    
    //万物皆信号。对什么感兴趣，什么就是信号
    
    //监听selector
    /**
     打印：
     收到信号 <RACTuple: 0x60000218e180> (
     "<UIButton: 0x7fad9911dce0; frame = (102 92.6667; 46 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x6000023cd8e0>>"
     )
     */
    [[self.hrView rac_signalForSelector:@selector(hahaAction:)] subscribeNext:^(RACTuple * _Nullable x) {
        NSLog(@"收到信号 %@", x);
    }];
    
    //
    //==========KVO===========
    //封装苹果KVO
    //    [self.hrView rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
    //
    //        NSLog(@"变化了");
    //    }];
    
    //监听属性frame
//    [[self.hrView rac_valuesForKeyPath:@"frame" observer:nil] subscribeNext:^(id  _Nullable x) {
//        NSLog(@"frame属性：%@",x);
//    }];
    //========================
    //监听属性frame（这种宏定义的方式最普遍）
    [RACObserve(self.hrView, frame) subscribeNext:^(id  _Nullable x) {
        NSLog(@"frame属性：%@",x);
    }];
    
    //UIControlEventTouchUpInside是监听的信号
    [[self.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
        NSLog(@"btn信号 %@", x);
    }];
    
    //通知是信号
    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
        
        NSLog(@"键盘弹出：%@",x);
    }];
    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"CustomNotification" object:nil] subscribeNext:^(NSNotification * _Nullable x) {
        
        NSLog(@"自定义通知：%@",x);
    }];
    
    //textField文本框是信号，每输入一个字符，回调获取一次文本框内容
//    [self.textfield.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
//        NSLog(@"textfield接收：%@", x);
//    }];
    
    //带filter的文本框，满足条件后，每输入一个字符，回调获取一次文本框
    [[self.textfield.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
        return value.length>3;
    }] subscribeNext:^(NSString * _Nullable x) {
        NSLog(@"textField(带filter)接收：%@", x);
    }];
    
    // 只要文本框文字改变，就会修改label的文字
    RAC(self.label, text) = _textfield.rac_textSignal;
    
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] init];
    [[tapGesture rac_gestureSignal] subscribeNext:^(id x) {
        NSLog(@"触发tap手势：%@", x);
    }];
    [self.hrView addGestureRecognizer:tapGesture];
    
    
    /**
     2020-01-21 22:38:58.676885+0800 RAC_learn[1273:25743] 遍历后的数组45
     2020-01-21 22:38:58.677481+0800 RAC_learn[1273:25743] 遍历后的数组23
     2020-01-21 22:38:58.677507+0800 RAC_learn[1273:25741] key=name, value=xomg
     2020-01-21 22:38:58.677865+0800 RAC_learn[1273:25743] 遍历后的数组54
     2020-01-21 22:38:58.678053+0800 RAC_learn[1273:25743] 遍历后的数组65
     2020-01-21 22:38:58.678207+0800 RAC_learn[1273:25741] key=age, value=18
     */
    //遍历数组
    NSArray *numbers = @[@"45",@"23",@"54",@"65"];
    [numbers.rac_sequence.signal subscribeNext:^(id x) {
        NSLog(@"遍历后的数组%@",x);
    }];
    
    //遍历字典
    NSDictionary *dict = @{@"name":@"xomg",@"age":@"18"};
    [dict.rac_sequence.signal subscribeNext:^(id x) {
        RACTupleUnpack_(NSString *key,NSString *value) = x;
        NSLog(@"key=%@, value=%@",key,value);
    }];
}

-(void)viewActionSignalDemo{
    [self.hrView.subjectSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"==%@", x);
    }];
}

-(void)mapDemo{
    
    /**
     2020-02-15 15:33:31.705436+0800 RAC_learn[3074:101490] 发送源信号
     2020-02-15 15:33:31.705621+0800 RAC_learn[3074:101490] mapBlock内部
     2020-02-15 15:33:31.705857+0800 RAC_learn[3074:101490] mapDemo接收到数据: haha66666
     2020-02-15 15:33:31.706081+0800 RAC_learn[3074:101490] ====
     2020-02-15 15:33:31.706217+0800 RAC_learn[3074:101490] 发送complete
     */
    
    //1.创建源信号
    RACSignal *originalSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        NSLog(@"发送源信号");
        
        //4.发送源信号
        // 对map来说，sendNext会回调mapBlock
        [subscriber sendNext:@"haha"];
        NSLog(@"====");
        [subscriber sendCompleted];
        NSLog(@"发送complete");
        return [RACDisposable disposableWithBlock:^{
            
        }];
    }];
    
    //2.绑定源信号，生成绑定信号
    RACSignal *testBindSignal = [originalSignal map:^id _Nullable(id  _Nullable value) {
        NSLog(@"mapBlock内部");
        // return 完会回调subscribeNextBlock
        return [NSString stringWithFormat:@"%@66666", value];
    }];
    
    //3.订阅绑定信号  【注意区分，这里订阅的是绑定信号，而不是源信号】
    [testBindSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"mapDemo接收到数据: %@", x);
    }];
}

-(void)bindDemo{
    
    // RAC很多复杂api都是基于bind操作的，理解透这个很重要
    // 源码详解参见：https://juejin.im/post/57caf01b5bbb500074ebddf6
    
    /** 输出
     2019-12-10 22:52:48.547047+0800 RAC基本使用[2319:56416] 订阅绑定信号
     2019-12-10 22:52:48.548420+0800 RAC基本使用[2319:56416] 订阅绑定信号了才来到这里
     2019-12-10 22:52:48.550344+0800 RAC基本使用[2319:56416] 发送源信号
     2019-12-10 22:52:48.550649+0800 RAC基本使用[2319:56416] 源信号发送值了就会来到这里
     2019-12-10 22:52:48.551028+0800 RAC基本使用[2319:56416] 接收到数据haha66666
     2019-12-10 22:52:48.551241+0800 RAC基本使用[2319:56416] ====
     2019-12-10 22:52:48.551438+0800 RAC基本使用[2319:56416] 发送complete
     
     主要以下流程：
     1、创建源信号源. 把createSignalBlock保存到【源信号的didSubscribe block】属性
     2、绑定源信号，生成绑定信号.
     -2.1 [originalSignal bind:] 内部创建绑定信号并返回，把bindBlock保存到【绑定信号的didSubscribe block】属性
     3、订阅绑定信号
     -3.1 [testBindSignal subscribeNext:] 内部创建绑定信号的订阅者，并且把nextBlock保存到【绑定信号订阅者next block】属性
     -3.2 self.didSubscribe(subscriber) 即执行流程二【绑定信号的didSubscribe block】块内容，该block块里面的self指originalSignal. 现在回到流程二bind中看
     -3.2.1 [self subscribeNext:]，这时才订阅源信号。内部创建源信号的订阅者，
     nextblock保存到【源信号订阅者next block】属性。
     -3.2.2 self.didSubscribe(subscriber) 即执行【源信号的didSubscribe block】块内容
     4、发送源信号
     -4.1 sendNext: 内部执行【源信号订阅者的next block】块内容，block块内容如下
     -4.2 执行bindingBlock(x, &stop)，也就是bind的^RACSignal *(id value, BOOL *stop){}回调，value为4.1 sendNext传递过来的参数。
     参数可做处理，然后传递到[RACReturnSignal return:]，
     内部创建了RACReturnSignal信号，且保存了value。
     （这里我认为创建RACReturnSignal目的是，returnSignal可以在订阅时就同步发送）
     -4.3 执行addSignal(returnSignal)，addSignal block内部调用了subscribeNext:，即订阅了returnSignal。returnSignal在订阅时就会同步去发送，所以直接进入nextBlock内部。
     其中有个[subscriber sendNext:x]，而这个subscriber是指绑定信号的订阅者，
     也就是去执行【绑定信号的订阅者next block】
     5、取消订阅（不再赘述）
     */
    
    //1.创建源信号
    RACSignal *originalSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        NSLog(@"发送源信号");
        
        //4.发送源信号
        [subscriber sendNext:@"haha"];
        NSLog(@"====");
        [subscriber sendCompleted];
        NSLog(@"发送complete");
        return [RACDisposable disposableWithBlock:^{
            
        }];
    }];
    
    //2.绑定源信号，生成绑定信号
    RACSignal *testBindSignal = [originalSignal bind:^RACSignalBindBlock _Nonnull{
        NSLog(@"订阅绑定信号了才来到这里");
        return ^RACSignal *(id value, BOOL *stop){
            NSLog(@"源信号发送值了就会来到这里");
            return [RACReturnSignal return:[NSString stringWithFormat:@"%@66666",value]];
        };
    }];
    
    NSLog(@"订阅绑定信号");
    //3.订阅绑定信号
    [testBindSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"bindDemo接收到数据%@", x);
    }];
}

-(void)commandDemo{
    
    /** 输出
     2020-01-21 17:22:47.824312+0800 RAC_learn[1190:25838] 正在执行
     2020-01-21 17:22:47.824708+0800 RAC_learn[1190:25838] 发送input:good morning
     2020-01-21 17:22:47.826828+0800 RAC_learn[1190:25838] 接收到数据good morning
     2020-01-21 17:22:47.827077+0800 RAC_learn[1190:25838] ====
     2020-01-21 17:22:47.830842+0800 RAC_learn[1190:25838] 发送complete
     2020-01-21 17:22:47.832883+0800 RAC_learn[1190:25838] 信号被销毁
     2020-01-21 17:22:47.836546+0800 RAC_learn[1190:25838] 执行完成
     */
    
    //1. 创建命令返回的是信号
    RACCommand *testCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
        
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            
            NSLog(@"发送input:%@", input);
            [subscriber sendNext:input];
            NSLog(@"====");
            //必须调用sendCompleted，命令才会执行完毕
            [subscriber sendCompleted];
            NSLog(@"发送complete");
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"信号被销毁");
            }];
        }];
    }];
    
    //2. 订阅命令发出的信号
    [testCommand.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {
        NSLog(@"接收到数据%@", x);
    }];
    
    
    //3.监听命令是否执行完毕，默认会来一次，我们跳过没有意义的第一次，即skip填1
    //要想命令执行完，上面必须调用sendCompleted
    [[testCommand.executing skip:1] subscribeNext:^(id x) {
        if ([x boolValue] == YES) {
            // 正在执行
            NSLog(@"正在执行");
            
        }else{
            // 执行完成
            NSLog(@"执行完成");
        }
    }];
    
    //4. 执行命令
    [testCommand execute:@"good morning"];
}

-(void)subjectDemo{
    //1.创建信号. 创建信号对象，然后创建一个可变数组
    RACSubject *subject = [RACSubject subject];
    
    //2.订阅信号. 创建一个订阅者，将nextBlock保存到订阅者的next block属性，将订阅者保存到上面的数组中
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"111%@", x);
    }];
    
    //【区别】RACSubject与RACSignal区别在于，RACSubject可以多次订阅
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"222%@", x);
    }];
    
    //3.发送信号. 遍历信号对象中的数组，取出订阅者。调用订阅者的next block执行
    [subject sendNext:@"hello, man"];
}

-(void)signalDemeo{
    
    /** 输出
     2019-12-10 22:22:47.947473+0800 RAC基本使用[1906:44290] 订阅信号，也就是监听信号
     2019-12-10 22:22:47.947865+0800 RAC基本使用[1906:44290] 发送信号
     2019-12-10 22:22:47.948102+0800 RAC基本使用[1906:44290] 接收到数据:1
     2019-12-10 22:22:47.948218+0800 RAC基本使用[1906:44290] ----完成信号收发----
     2019-12-10 22:22:47.948363+0800 RAC基本使用[1906:44290] 信号被销毁，释放资源
     */
    
    //1.创建信号. 把createSignalBlock保存到信号的didSubscribe block属性
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        //block调用时刻：每当有订阅者订阅信号，就会调用block
        NSLog(@"发送信号");
        //3.由订阅者发送信号. 调用订阅者中的next block执行
        [subscriber sendNext:@1];
        NSLog(@"----完成信号收发----");
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"信号被销毁，释放资源");
        }];
    }];
    
    //2.订阅信号,才会激活信号. 创建一个订阅者，把nextBlock保存到订阅者next block属性，调用上面信号中的didSubscribe block执行
    NSLog(@"订阅信号，也就是监听信号");
    [signal subscribeNext:^(id  _Nullable x) {
        // block调用时刻：每当有信号发出数据，就会调用block.
        NSLog(@"接收到数据:%@",x);
    }];
}

@end
